#include "CannonBall.h"

#include "../../Game.h"
#include "../../PhysicsLogic.h"
#include "../../Textures.h"
#include "../../Renderer.h"
#include "../../ShapeData.h"

SurfaceRenderingData CannonBall::srd;

ArrayList<CannonBall*> CannonBall::all;
LinkedList<CannonBall*> CannonBall::toDelete;

unsigned int CannonBall::displayList;
const float CannonBall::radius = 0.20f;

void CannonBall::Load() { //static
	const float color = 0.35f;
	const float specular = 1.0f;
	srd.SetColor(color, color, color, 1.0f);
	srd.SetSpecularMaterial(specular, specular, specular, 1.0f);
	srd.SetTexture(Textures::TEXTURE_CANNON_BALL);

	displayList = glGenLists(1);

	glNewList(displayList, GL_COMPILE);
	gluSphere(Renderer::GetQuadric(), radius, 32, 32);
	glEndList();
}

void CannonBall::DeleteDestroyed() { //static
	LinkedList<CannonBall*>& destroyedCannonBalls = CannonBall::toDelete;
	while(!destroyedCannonBalls.Empty()) {
		CannonBall* cannonBall = destroyedCannonBalls.GetFirst();
		delete cannonBall;
		destroyedCannonBalls.RemoveFirst();
		CannonBall::all.Remove(cannonBall);
	}
}

CannonBall::CannonBall(Player* owner, const NxVec3& pos, const NxVec3& initialVelocity, const NxVec3& windForce, bool explosive) {
	Game::log.LogDebug(StringBuilder() << "CannonBall " << this);

	this->owner = owner;
	this->explosive = explosive;
	this->windForce = windForce;

	NxScene* scene = PhysicsLogic::GetScene();
	if(scene == NULL) {
		Game::log.LogError("Creating cannon ball with scene == NULL");
		actor = NULL;
	} else {
		NxBodyDesc bodyDesc;
		bodyDesc.angularDamping	= 0.5f;
		bodyDesc.linearVelocity = initialVelocity;

		NxSphereShapeDesc sphereDesc;
		sphereDesc.radius = radius;

        ShapeData *sd = new ShapeData();
        sphereDesc.userData = sd;

		NxActorDesc actorDesc;
		actorDesc.shapes.pushBack(&sphereDesc);
		actorDesc.body			= &bodyDesc;
		actorDesc.density		= 100.0f;
		actorDesc.globalPose.t  = pos;

		actor = scene->createActor(actorDesc);
		actor->userData = this;
		actor->setGroup(1);
		actor->setContactReportFlags(NX_NOTIFY_FORCES);

		//CCD
		PhysicsLogic::MakeCCD(actor->getShapes()[0]);
		actor->setCCDMotionThreshold(10.0f);

		PhysicsLogic::SetObservedCannonBall(this);
	}

	all.Add(this);
}

CannonBall::~CannonBall() {
	NxScene* scene = PhysicsLogic::GetScene();
	if(scene == NULL || actor == NULL) {
		Game::log.LogError("Deleting cannon ball with scene == NULL || actor == NULL");
	} else {
		NxShape* const* shapes = actor->getShapes();
		NxU32 shapesCount = actor->getNbShapes();
		for(NxU32 i=0; i<shapesCount; ++i) {
			NxShape* shape = shapes[i];
			if(shape->userData) {
				delete shape->userData;
			}
		}
		scene->releaseActor(*actor);
	}

	if(PhysicsLogic::GetObservedCannonBall() == this) {
		PhysicsLogic::SetObservedCannonBall(NULL);
	}

	Game::log.LogDebug(StringBuilder() << "~CannonBall " << this);
}

void CannonBall::Draw() {
    bool cannonBallIsVisible = false;
    NxShape* const* shapes = actor->getShapes();
    NxU32 shapesCount = actor->getNbShapes();
    for(NxU32 i=0; i<shapesCount; ++i) {
        NxShape* shape = shapes[i];
        if(shape->userData) {
            ShapeData *sd = (ShapeData*)shape->userData;
            cannonBallIsVisible |= sd->isVisible;
            sd->isVisible = false;
        }
    }

    if(cannonBallIsVisible) {
        float m[16];

        glPushMatrix();
        actor->getGlobalPose().getColumnMajor44(m);
        glMultMatrixf(m);
        Renderer::SetMaterial(srd);
        glCallList(displayList);
        glPopMatrix();

        glDisable(GL_LIGHTING);
        glPushMatrix();
        const float randomShadowHeight = 0.00001f*(rand()%1000);
        const static float shadowMatrix[] = {1,0,0,0, 0,randomShadowHeight,0,0, 0,0,1,0, 0,0,0,1};
        glMultMatrixf(shadowMatrix);
        glMultMatrixf(m);
        Renderer::SetMaterial(Renderer::shadowMaterial);
        glCallList(displayList);
        glPopMatrix();
        glEnable(GL_LIGHTING);
    }	
}

void CannonBall::Tick() {
	actor->addForce(windForce*0.10f);	
}
